/*******************************************************************************
 * FilePath    : /Examples_Ble_MS1658/ble_app_master_uart/Src/BLE/app.c
 * Version     : 1.0
 * Brief       :
 * Author      : AndrewHu
 * Company     : Shanghai MacroGiga Electronics CO.,Ltd
 * Email       : Hubinbo@macrogiga.com
 * Date        : 2020-04-03 15:06:14
 * LastEditors : AndrewHu
 * LastEditTime: 2021-12-28 12:19:52
 * Description :
 ******************************************************************************/
/* Includes ------------------------------------------------------------------*/
#include <Includes.h>
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
#define ATT_CHAR_PROP_RD 0x02
#define ATT_CHAR_PROP_W_NORSP 0x04
#define ATT_CHAR_PROP_W 0x08
#define ATT_CHAR_PROP_NTF 0x10
#define ATT_CHAR_PROP_IND 0x20

#define GATT_PRIMARY_SERVICE_UUID 0x2800
#define GATT_SECONDARY_SERVICE_UUID 0x2801
#define TYPE_INC 0x2802
#define TYPE_CHAR 0x2803
#define GATT_CHAR_EXT_PROP 0x2900
#define TYPE_INFO 0x2901
#define TYPE_CFG 0x2902
#define TYPE_SRV_CFG 0x2903
#define TYPE_CHAR_FMT 0x2904
#define TYPE_CHAR_AGG 0x2905
#define TYPE_xRpRef 0x2907
#define TYPE_RpRef 0x2908

#define UUID16_FORMAT 0xff

#define MANU_INFO "MacroGiga Bluetooth"
#define SOFTWARE_INFO (__DATE__ " "__TIME__)
#define MAX_NAME_LEN 24
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
u16 cur_notifyhandle = BleRxHandle; // Note: make sure each notify handle by invoking function: set_notifyhandle(hd);
u8 gConnectedFlag = 0;
/*first byte is len, max len is 24*/
u8 DeviceInfo[MAX_NAME_LEN + 1] = {
    11,                                //设备名称长度，最长24个字节
    'M', 'S', '1', '6', '5', '8', '-', //从Flash数据中读取设备名，如果设备名有修改，将存入flash
    'U', 'A', 'R', 'T',                //
    0x00,                              //
};

static u32 SCount = 0; //
/* Private function ----------------------------------------------------------*/
/* extern --------------------------------------------------------------------*/
/*******************************************************************************
 * Function    : getDeviceInfoData
 * Brief       :
 * Parameter   :
 * Returns     :
 * Date        : 2020-04-03 17:54:39
 * Description :
 ******************************************************************************/
u8 *getDeviceInfoData(u8 *len)
{
    *len = DeviceInfo[0];
    return (u8 *)&DeviceInfo[1];
}
/*******************************************************************************
 * Function    : updateDeviceInfoData
 * Brief       :
 * Parameter   :
 * Returns     :
 * Date        : 2020-04-03 17:54:58
 * Description :
 ******************************************************************************/
void updateDeviceInfoData(u8 *name, u8 len)
{
    if (len > MAX_NAME_LEN)
        len = MAX_NAME_LEN;
    DeviceInfo[0] = len;
    memcpy(&DeviceInfo[1], name, len);
    ble_set_name(name, len);
}
/*******************************************************************************
 *****DataBase****
 *******************************************************************************/
typedef struct ble_character16
{
    u16 type16;          // type2
    u16 handle_rec;      // handle
    u8 characterInfo[5]; // property1 - handle2 - uuid2
    u8 uuid128_idx;      // 0xff means uuid16,other is idx of uuid128
} BLE_CHAR;
typedef struct ble_UUID128
{
    u8 uuid128[16]; // uuid128 string: little endian
} BLE_UUID128;
/*******************************************************************************
 * Function    :
 * Brief       :
 * Parameter   :
 * Returns     :
 * Date        : 2020-03-23 15:23:42
 * Description : STEP0:Character declare
 ******************************************************************************/
const BLE_CHAR AttCharList[] = {
    // ======  gatt =====  Do NOT Change!!
    {GATT_PRIMARY_SERVICE_UUID, 0x0001},                                         //服务1
    {TYPE_CHAR, 0x0002, {ATT_CHAR_PROP_RD, 0x03, 0, 0x00, 0x2a}, UUID16_FORMAT}, // name，设备名
    {GATT_PRIMARY_SERVICE_UUID, 0x0004},                                         //服务2
    // 05-06 reserved
    // ======  device info =====    Do NOT Change if using the default!!!
    {GATT_PRIMARY_SERVICE_UUID, 0x0007},                                         //服务3
    {TYPE_CHAR, 0x0008, {ATT_CHAR_PROP_RD, 0x09, 0, 0x29, 0x2a}, UUID16_FORMAT}, // manufacture
    {TYPE_CHAR, 0x000a, {ATT_CHAR_PROP_RD, 0x0b, 0, 0x26, 0x2a}, UUID16_FORMAT}, // firmware version
    {TYPE_CHAR, 0x000c, {ATT_CHAR_PROP_RD, 0x0d, 0, 0x28, 0x2a}, UUID16_FORMAT}, // sw version

    // ======  User service or other services added here =====  User defined
    {GATT_PRIMARY_SERVICE_UUID, 0x000e},                                                               //服务4
    {TYPE_CHAR, 0x000f, {ATT_CHAR_PROP_NTF, BleRxHandle, 0, 0, 0}, 1 /*uuid128-idx1*/},                // RxNotify
    {TYPE_CFG, 0x0011, {ATT_CHAR_PROP_RD | ATT_CHAR_PROP_W}},                                          // cfg
    {TYPE_CHAR, 0x0012, {ATT_CHAR_PROP_W | ATT_CHAR_PROP_W_NORSP, 0x13, 0, 0, 0}, 2 /*uuid128-idx2*/}, // Tx
};

const BLE_UUID128 AttUuid128List[] = {
    /*for supporting the android app [nRF UART V2.0], one SHOULD using the 0x9e,0xca,0xdc.... uuid128*/

    //蓝牙串口
    {0x9e, 0xca, 0xdc, 0x24, 0x0e, 0xe5, 0xa9, 0xe0, 0x93, 0xf3, 0xa3, 0xb5, 1, 0, 0x40, 0x6e}, // idx0,little endian, service uuid
    {0x9e, 0xca, 0xdc, 0x24, 0x0e, 0xe5, 0xa9, 0xe0, 0x93, 0xf3, 0xa3, 0xb5, 3, 0, 0x40, 0x6e}, // idx1,little endian, RxNotify
    {0x9e, 0xca, 0xdc, 0x24, 0x0e, 0xe5, 0xa9, 0xe0, 0x93, 0xf3, 0xa3, 0xb5, 2, 0, 0x40, 0x6e}, // idx2,little endian, Tx
};
u8 GetCharListDim(void)
{
    return sizeof(AttCharList) / sizeof(AttCharList[0]);
}
/*******************************************************************************
 * Function    : att_server_rdByGrType
 * Brief       :
 * Parameter   :
 * Returns     :
 * Date        : 2020-03-23 15:24:25
 * Description : STEP1:Service declare
 *               read by type request handle, primary service declare implementation
 *               蓝牙握手，获取服务句柄、句柄范围、以及服务UUID
 ******************************************************************************/
void att_server_rdByGrType(u8 pdu_type, u8 attOpcode, u16 st_hd, u16 end_hd, u16 att_type)
{
    //!!!!!!!!  hard code for gap and gatt, make sure here is 100% matched with database:[AttCharList] !!!!!!!!!

    if ((att_type == GATT_PRIMARY_SERVICE_UUID) && (st_hd == 0x0001)) // hard code for device info service
    {
        // att_server_rdByGrTypeRspDeviceInfo(pdu_type);//using the default device info service
        u8 t[] = {0x00, 0x18};
        att_server_rdByGrTypeRspPrimaryService(pdu_type, 0x01, 0x03, (u8 *)(t), 2);
        return;
    }

    if ((att_type == GATT_PRIMARY_SERVICE_UUID) && (st_hd == 0x0004)) // hard code for device info service
    {
        u8 t[] = {0x01, 0x18};
        att_server_rdByGrTypeRspPrimaryService(pdu_type, 0x04, 0x06, (u8 *)(t), 2);
        return;
    }

    else if ((att_type == GATT_PRIMARY_SERVICE_UUID) && (st_hd <= 0x0007)) // hard code for device info service
    {
        // apply user defined (device info)service example
        u8 t[] = {0x0a, 0x18};
        att_server_rdByGrTypeRspPrimaryService(pdu_type, 0x07, 0x0d, (u8 *)(t), 2);
        return;
    }

    else if ((att_type == GATT_PRIMARY_SERVICE_UUID) && (st_hd <= 0x000e)) // usr's service
    {
        // u8 t[] = {0x1c, 0x18};
        // att_server_rdByGrTypeRspPrimaryService(pdu_type, 0x0e, 0x13, (u8 *)(t), 2);
        att_server_rdByGrTypeRspPrimaryService(pdu_type, 0x0e, 0x13, (u8 *)(AttUuid128List[0].uuid128), 16);
        return;
    }
    // other service added here if any
    // to do....

    /// error handle
    att_notFd(pdu_type, attOpcode, st_hd);
}
/*******************************************************************************
 * Function    : ser_write_rsp
 * Brief       :
 * Parameter   :
 * Returns     :
 * Date        : 2020-03-23 15:49:53
 * Description : STEP2:data coming，主机(手机)执行写数据命令
 * 凡是有ATT_CHAR_PROP_W或ATT_CHAR_PROP_W_NORSP属性的特征值都要根据对饮的句柄(att_hd)来操作
 ******************************************************************************/
void ser_write_rsp(u8 pdu_type /*reserved*/, u8 attOpcode /*reserved*/,
                   u16 att_hd, u8 *attValue /*app data pointer*/, u8 valueLen_w /*app data size*/)
{
    switch (att_hd)
    {
    case 0x13: // BLE透传，主机（手机）写入数据
        //串口打印数据,如果发过来的数据一次超过20个字节将在ser_prepare_write函数内接收
        Uart_Send_String(attValue, valueLen_w);
        ser_write_rsp_pkt(pdu_type);
        break;
    case 0x11:
        ser_write_rsp_pkt(pdu_type);
        break;
    default:
        att_notFd(pdu_type, attOpcode, att_hd); /*the default response, also for the purpose of error robust */
        break;
    }
}

/// STEP2.1:Queued Writes data if any
void ser_prepare_write(u16 handle, u8 *attValue, u16 attValueLen, u16 att_offset) // user's call back api
{
    // queued data:offset + data(size)
    // when ser_execute_write() is invoked, means end of queue write.

    // to do
    if (handle == 0x13)
    {
        Uart_Send_String(attValue, attValueLen);
    }
}

void ser_execute_write(void) // user's call back api
{
    // end of queued writes

    // to do...
}
/*******************************************************************************
 * Function    : server_rd_rsp
 * Brief       :
 * Parameter   :
 * Returns     :
 * Date        : 2020-03-23 16:07:58
 * Description : STEP3:Read data，read response
 * 主机（手机）读取数据时，蓝牙的反馈包（返回给手机读取内容）
 ******************************************************************************/
void server_rd_rsp(u8 attOpcode, u16 attHandle, u8 pdu_type)
{
    u8 d_len;
    u8 *ble_name = getDeviceInfoData(&d_len);

    switch (attHandle) // hard code
    {
    case 0x03: // GAP name
        att_server_rd(pdu_type, attOpcode, attHandle, ble_name, d_len);
        break;

    case 0x09: // MANU_INFO
        att_server_rd(pdu_type, attOpcode, attHandle, (u8 *)(MANU_INFO), sizeof(MANU_INFO) - 1);
        // att_server_rd(pdu_type, attOpcode, attHandle, get_ble_version(), strlen((const char *)get_ble_version())); //ble lib build version
        break;

    case 0x0b: // FIRMWARE_INFO
    {
        // do NOT modify this code!!!
        att_server_rd(pdu_type, attOpcode, attHandle, GetFirmwareInfo(), strlen((const char *)GetFirmwareInfo()));
        break;
    }

    case 0x0d: // SOFTWARE_INFO
        att_server_rd(pdu_type, attOpcode, attHandle, (u8 *)(SOFTWARE_INFO), sizeof(SOFTWARE_INFO) - 1);
        break;

    case 0x11: // cfg
    {
        u8 t[2] = {0, 0};
        att_server_rd(pdu_type, attOpcode, attHandle, t, 2);
        break;
    }

    default:
        att_notFd(pdu_type, attOpcode, attHandle); /*the default response, also for the purpose of error robust */
        break;
    }
}

void server_blob_rd_rsp(u8 attOpcode, u16 attHandle, u8 dataHdrP, u16 offset)
{
}

// return 1 means found
int GetPrimaryServiceHandle(unsigned short hd_start, unsigned short hd_end,
                            unsigned short uuid16,
                            unsigned short *hd_start_r, unsigned short *hd_end_r)
{
    // example
    // if ((uuid16 == 0xca9e) && (hd_start <= 0x10)) // MUST keep match with the information save in function  att_server_rdByGrType(...)
    // {
    //     *hd_start_r = 0x10;
    //     *hd_end_r = 0x19;
    //     return 1;
    // }

    return 0;
}

/*******************************************************************************
 * Function    :
 * Brief       :
 * Parameter   :
 * Returns     :
 * Date        : 2020-03-23 16:15:32
 * Description :
 * 本回调函数可用于蓝牙模块端主动发送数据之用，协议栈会在系统允许的时候（异步）回调本函数，不得阻塞！！
 * 补充：蓝牙基本每次和手机交互时都会从这里回调，所以要在该函数内判断，有数据需要发送时才写入数据
 ******************************************************************************/
void gatt_user_send_notify_data_callback(void)
{
    // to do if any ...
    // add user sending data notify operation ....
    NotifyRun(); //从机发送
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////
// u8* getsoftwareversion(void)
//{
//     return (u8*)SOFTWARE_INFO;
// }
////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*******************************************************************************
 * Function    : GetConnectedStatus
 * Brief       : 获取连接状态
 * Parameter   :
 * Returns     :
 * Date        : 2020-04-06 01:44:20
 * Description :
 ******************************************************************************/
char GetConnectedStatus(void)
{
    return gConnectedFlag;
}

void ConnectStausUpdate(unsigned char IsConnectedFlag) // porting api
{
    //[IsConnectedFlag] indicates the connection status
    //  0 - peripheral/central idle
    //  1 - peripheral connected
    //  2 - central connected

    if (2 == IsConnectedFlag) // master connected
    {
        // discover all services if any, one may get the handle info @ UsrProcCallback_Central(0x81,...)
        ble_master_serviceDiscover(1);
        // ble_master_charDiscover(1,0xff);//discover all characters if any(for all services)
    }

    if (IsConnectedFlag != gConnectedFlag)
    {
        if ((2 == gConnectedFlag) && (0 == IsConnectedFlag))
        { //主机断开连接
            Uart_Send_String((u8 *)"\r\nM_Disconnect", 14);
            SCount = 0; //主从切换计时清零
        }
        else if ((1 == gConnectedFlag) && (0 == IsConnectedFlag))
        { //从机断开连接
            Uart_Send_String((u8 *)"\r\nS_Disconnect", 14);
            SCount = 0; //主从切换计时清零
        }
        gConnectedFlag = IsConnectedFlag;

        if (2 == gConnectedFlag)
        {
            Uart_Send_String((u8 *)"\r\nM_Connect", 11);
        }
        else if (1 == gConnectedFlag)
        {
            Uart_Send_String((u8 *)"\r\nS_Connect", 11);
        }
    }
}
/*******************************************************************************
 * Function   :      UsrProcCallback
 * Parameter  :      void
 * Returns    :      void
 * Description:
 * Note:      :      回调函数
 *******************************************************************************/
void UsrProcCallback(void) // porting api
{
    static u16 counter = 0;

    WDG_ReloadCounter(); //清狗
    if (GetConnectedStatus())
        setLedPinStatus(1); //连接，常亮
    else
        LED_ChangeStatus(); //闪灯

    if (0 == GetConnectedStatus())
    { //非连接状态
        SCount++;
        if (SCount > 30)
        {
            SCount = 0;
            ble_set_role(1, 104); // 切换为主机
            ble_set_scan(280, 1600);
        }
    }

//更新连接间隔
#if 0
    if(GetConnectedStatus()){
        counter ++;
        if(counter == 200){
            conn_interv = sconn_GetConnInterval();
            if(conn_interv > 24){//24*1.25=30ms
                SIG_ConnParaUpdateReq(0x0006, 0x0010, 0x0000, 1000);
            }
            counter = 0;
        }
    }else{
        counter = 0;
    }
#endif
}
/*******************************************************************************
 * Function   :      UsrProcCallback_Central
 * Parameter  :      void
 * Returns    :      void
 * Description:      主机模式回调函数
 * Note:      :      回调函数
 *******************************************************************************/
// u8 dev_adva[6] = { //待连接蓝牙设备地址
//     0xdd, 0xe5, 0xbb, 0x39, 0x67, 0xED};
void UsrProcCallback_Central(u8 fin, u8 *dat_rcv, u8 dat_len)
{
    u8 i = 0;

    WDG_ReloadCounter(); //清狗
    if (GetConnectedStatus())
        setLedPinStatus(1); //连接
    else
        LED_ChangeStatus(); //闪灯

#if 0
    //打印广播信息
    if ((fin < 0x80) && (dat_len > 0))
    {
        // if (AHL_Data_Compare(dev_adva, &dat_rcv[2], 6))
        // { //地址过滤
        Printf_NewLine();
        Uart_Send_String((u8 *)"\r\nrssi:-", 8);
        Uart_Send_Byte2Ascii(235 - GetRssiData());

        Uart_Send_String((u8 *)"\r\nScanned ", 10);
        Uart_Send_Byte2Ascii(dat_len);
        Uart_Send_String((u8 *)" Bytes", 6);

        Uart_Send_String((u8 *)"\r\nHeader:", 9);
        Uart_Send_Array2Ascii(dat_rcv, 2, TRUE);

        Uart_Send_String((u8 *)"\r\nMac:", 6);
        Uart_Send_Array2Ascii(&dat_rcv[2], 6, TRUE);

        Uart_Send_String((u8 *)"\r\nADV:", 6);
        Uart_Send_Array2Ascii(&dat_rcv[8], dat_rcv[1] - 6, TRUE);

        i = dat_rcv[1] + 2;
        if (dat_len > i)
        {
            Uart_Send_String((u8 *)"\r\nScan Response:", 16);
            Uart_Send_Array2Ascii(&dat_rcv[i], dat_len - i, TRUE);
        }
        // }
    }
    //可以在此处设置待连接的蓝牙mac地址
    // ble_master_setTargetDeviceAddr(dat_rcv + 2);
#endif

    //打印从机服务
    if ((fin >= 0x80) && (fin <= 0x91))
    {
        if (dat_len)
        {
            if (0x81 == fin)
            {
                Uart_Send_String((u8 *)"\r\nService:\r\n", 12);

                Uart_Send_Array2Ascii(dat_rcv, 2, TRUE);
                Uart_Send_String((u8 *)"(start_hdl)", 11);

                Uart_Send_Array2Ascii(&dat_rcv[2], 2, TRUE);
                Uart_Send_String((u8 *)"(end_hdl)", 9);

                Uart_Send_Array2Ascii(&dat_rcv[4], dat_len - 4, TRUE);
                Uart_Send_String((u8 *)"(UUID)\r\n", 8);
            }
            else if (0x91 == fin)
            {
                Uart_Send_String((u8 *)"\r\nCharacter:\r\n", 14);

                Uart_Send_Array2Ascii(dat_rcv, 2, TRUE);
                Uart_Send_String((u8 *)"(handle)", 8);

                Uart_Send_Array2Ascii(&dat_rcv[2], 1, TRUE);
                Uart_Send_String((u8 *)"(permision)", 11);

                Uart_Send_Array2Ascii(&dat_rcv[3], 2, TRUE);
                Uart_Send_String((u8 *)"(hdl)", 5);

                Uart_Send_Array2Ascii(&dat_rcv[5], dat_len - 5, TRUE);
                Uart_Send_String((u8 *)"(UUID)\r\n", 8);
            }
        }
    }

    if (fin == 0x80) // service not found,0x81 means service is found
    {
        ble_master_charDiscover(0x01, 0xff);
    }

    if (fin == 0x90) // character not found,0x91 means character is found
    {
    }

    if (0 == GetConnectedStatus())
    { //非连接状态
        SCount++;
        if (SCount > 100)
        {
            ble_set_role(0, 10); // 切换为从机
            SCount = 0;
        }
    }
}
/*******************************************************************************
 * Function   :      gatt_client_send_callback
 * Parameter  :      void
 * Returns    :      void
 * Description:      本回调函数可用于Central(client)端主动发送数据之用，
 *                   协议栈会在系统允许的时候（异步）回调本函数，不得阻塞！！
 * Note:      :      回调函数
 *******************************************************************************/
void gatt_client_send_callback(void)
{
    WDG_ReloadCounter(); //清狗

    NotifyRun(); //主机发送
}
void att_cli_receive_callback(u16 att_hd, u8 *attValue /*app data pointer*/, u8 valueLen_w /*app data size*/)
{
    if (cur_notifyhandle == att_hd)
    {
        // Uart_Send_Array2Ascii(attValue, valueLen_w, TRUE);
        Uart_Send_String(attValue, valueLen_w);
    }
    else
    {
        // Uart_Send_Array2Ascii(attValue, valueLen_w, TRUE);
        Uart_Send_String(attValue, valueLen_w);
    }
}
